package com.study.spaoplog.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @author MI
 * @version 1.0
 * @date 2021/6/4 21:53
 * SpringBoot查询日志：添加日志跟踪id
 * https://www.jianshu.com/p/08f5231c3d32
 */
@Slf4j
// @Aspect
@Component//没有这个切面不生效
public class SpringAOP {
    /**
     * 定义切点Pointcut
     * 第一个*号：表示返回类型， *号表示所有的类型
     * 第二个*号：表示类名，*号表示所有的类|“.*”表示包下的所有类，而“..*”表示包、子孙包下的所有类。
     * 第三个*号：表示方法名，*号表示所有的方法
     * 后面括弧里面表示方法的参数，两个句点表示任何参数
     */
    @Pointcut("execution(* com..*.controller.*.*(..))")
    public void executionService() {
    }

    /**
     * https://www.cnblogs.com/wangshen31/p/9379197.html
     * 然后看到注解下面的方法，就是描述advice的，我们看到有个参数 JoinPoint，这个东西代表着织入增强处理的连接点。JoinPoint包含了几个很有用的参数：
     * <p>
     * Object[] getArgs：返回目标方法的参数
     * Signature getSignature：返回目标方法的签名
     * Object getTarget：返回被织入增强处理的目标对象
     * Object getThis：返回AOP框架为目标对象生成的代理对象
     * </p>
     * <p>
     * 除了注解@Around的方法外，其他都可以加这个JoinPoint作参数。@Around注解的方法的参数一定要是 ProceedingJoinPoint，下面会介绍。
     * <p>
     * roceedingJoinPoint，这个对象是JoinPoint的子类。我们可以把这个看作是切入点的那个方法的替身，这个proceedingJoinPoint有个proceed（）方法，
     * 相当于就是那切入点的那个方法执行，简单地说就是让目标方法执行，然后这个方法会返回一个对象，这个对象就是那个切入点所在位置的方法所返回的对象。
     * </p>
     */
    /**
     * Spring Aop根据参数名称获取参数的值（JoinPoint根据参数名获取参数的值）
     * https://blog.csdn.net/qq_30038111/article/details/94406589
     * 其实就是上面的 getSignature 和 getArgs 的组合
     */
    @Before("executionService()")
    public void doBefore(JoinPoint joinPoint) {
        final String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);

        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取到的是参数名
        String[] strings = methodSignature.getParameterNames();
        log.info("参数名称:", Arrays.toString(strings));
        log.info("连接点-", joinPoint);
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("连接类名" + joinPoint.getSignature().getDeclaringType()
                + " 连接点方法为：" + methodName + ",参数为：" + args);
        log.info("----->@Before:请求参数：{}", Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * JoinPoint 放在参数第一位。
     */
    @AfterReturning(pointcut = "executionService()", returning = "returnValue")
    public void doAfterReturning(JoinPoint joinPoint, Object returnValue) {
        log.info("----->@AfterReturning:响应参数为{}", returnValue);
        MDC.remove("requestId");
        // System.out.println("SpringAOP aop之后的对象-"+joinPoint.getTarget());
        final Signature signature = joinPoint.getSignature();
    }
}
